#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
#endregion

namespace GameStateManagement
{
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class GameplayScreen : GameScreen
    {
        #region Fields

        ContentManager content;
        SpriteFont gameFont;
               
        Vector2 scorePosition = new Vector2(5, 5);
        Vector2 shieldPosition = new Vector2(650, 5);
        Vector2 boostPosition = new Vector2(650, 35);
        Vector2 baseHealthPosition = new Vector2(5, 35);
        Random random;

        StarField starField;

        Player player;
        
        Rectangle shipRect;
        Texture2D ship;
        Texture2D spriteSheet;  // this is for the star
        Texture2D shield;
        Texture2D explosionTexture;
        List<Animation> explosions;

        Texture2D enemyTexture;
        List<Enemy> enemies;

        TimeSpan enemySpawnTime;
        TimeSpan previousSpawnTime;

        Texture2D projectileTexture;
        //Animation projectileAnimation;
        List<projectile> projectiles;

        int maxProjectiles = 3;
        int maxEnemies = 1;
        
        TimeSpan fireTime;
        
        home1 station;

        bool end;

        turret turretA;
        bolt boltA;

        SoundEffect fire;
        SoundEffect bolthit;
        SoundEffect lilexp;
        SoundEffect bigexp;
        SoundEffect shieldHit;

        int endtime;

        float pauseAlpha;
        
        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen()
        {
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);
            

            player = new Player(); 
            station = new home1();
            turretA = new turret();
            boltA = new bolt();
            end = false;

            enemies = new List<Enemy>();
            shipRect = new Rectangle((int)player.position.X, (int)player.position.Y, 32, 32);

            previousSpawnTime = TimeSpan.Zero;

            enemySpawnTime = TimeSpan.FromSeconds(2.0f);

            projectiles = new List<projectile>();
            explosions = new List<Animation>();

            random = new Random();

            fireTime = TimeSpan.FromSeconds(1.0f);
         }


        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            gameFont = content.Load<SpriteFont>("gamefont");
            ship = content.Load<Texture2D>("ship");
            shield = content.Load<Texture2D>("shieldAni");
            spriteSheet = content.Load<Texture2D>("spriteSheet");
            player.setShield(shield); 

            enemyTexture = content.Load<Texture2D>("enemyship");
            starField = new StarField(ScreenManager.GraphicsDevice.Viewport.Width, ScreenManager.GraphicsDevice.Viewport.Height,
                200, new Vector2(0, 30f), spriteSheet, new Rectangle(0, 450, 2, 2));

            //projectileTexture = content.Load<Texture2D>("laser");
            projectileTexture = content.Load<Texture2D>("laserAnimation");
            
            explosionTexture = content.Load<Texture2D>("explosion");

            Vector2 main_basePosition = new Vector2(ScreenManager.GraphicsDevice.Viewport.TitleSafeArea.X, 
                ScreenManager.GraphicsDevice.Viewport.TitleSafeArea.Y + ScreenManager.GraphicsDevice.Viewport.TitleSafeArea.Height / 2);

            station.Initialize(content.Load<Texture2D>("main_base"), main_basePosition);
            boltA.Initialize(content.Load<Texture2D>("turretshot"), boltA.boltPosition);
            turretA.Initialize(content.Load<Texture2D>("turret"), turretA.turretPosition);

            fire = content.Load<SoundEffect>("teleport");
            bolthit = content.Load<SoundEffect>("gunshot5");
            lilexp = content.Load<SoundEffect>("snd_explosion1");
            bigexp = content.Load<SoundEffect>("snd_explosion2");
            shieldHit = content.Load<SoundEffect>("ESPARK1");

            //enemyColorArray = TextureTo2DArray(enemyTexture);
            //projectileColorArray = TextureTo2DArray(projectileTexture);
       
            Thread.Sleep(1000);

            ScreenManager.Game.ResetElapsedTime();
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            content.Unload();
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            starField.Update(gameTime);
            base.Update(gameTime, otherScreenHasFocus, false);

            // Gradually fade in or out depending on whether we are covered by the pause screen.
            if (coveredByOtherScreen)
                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
            else
                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);

            if (IsActive)
            {
                          
                player.update(gameTime);
              

               station.Update();
               turretA.Update();
               boltA.Update();
              
               UpdateEnemies(gameTime);
               
               UpdateProjectiles(gameTime);
               UpdateExplosions(gameTime);

               if(end == true)
               {
                   if (endtime + 3000 <= gameTime.TotalGameTime.TotalMilliseconds)
                   {
                       ScreenManager.AddScreen(new MainMenuScreen(), ControllingPlayer);
                   }
               }
                
               UpdateCollision(gameTime);
               if (turretA.Active == true)
               {
                   if(boltA.oldtime + 15000 <= gameTime.TotalGameTime.TotalMilliseconds)
                   {
                       boltA.shoot = true;
                       boltA.oldtime = (int)gameTime.TotalGameTime.TotalMilliseconds;
                   }

                   for (int z = 0; z <= projectiles.Count - 1; z++)
                   {
                       if (Vector2.Distance(turretA.turretPosition, projectiles[z].Position) <= 65 && boltA.shoot == true)
                       {
                           boltA.Active = true;
                           fire.Play();
                           turretA.Angle =  TurnToFace(turretA.turretPosition,projectiles[z].Position,turretA.Angle,10) + MathHelper.ToRadians(90);
                           boltA.shoot = false;
                       }
                       if (boltA.Active == true)
                       {
                           boltA.Angle = turretA.Angle;

                           boltA.boltPosition.X += ((float)Math.Sin(boltA.Angle)) * 5;
                           boltA.boltPosition.Y += -((float)Math.Cos(boltA.Angle)) * 5;

                           boltA.boltPosition.X += ((float)Math.Sin(boltA.Angle)) * 9;
                           boltA.boltPosition.Y += -((float)Math.Cos(boltA.Angle)) * 9;

                       }
                   }
               }

            }
        }
        private static float TurnToFace(Vector2 position, Vector2 faceThis,
        float currentAngle, float turnSpeed)
            {
                // consider this diagram:
                //         B 
                //        /|
                //      /  |
                //    /    | y
                //  / o    |
                // A--------
                //     x
                // 
                // where A is the position of the object, B is the position of the target,
                // and "o" is the angle that the object should be facing in order to 
                // point at the target. we need to know what o is. using trig, we know that
                //      tan(theta)       = opposite / adjacent
                //      tan(o)           = y / x
                // if we take the arctan of both sides of this equation...
                //      arctan( tan(o) ) = arctan( y / x )
                //      o                = arctan( y / x )
                // so, we can use x and y to find o, our "desiredAngle."
                // x and y are just the differences in position between the two objects.
                float x = faceThis.X - position.X;
                float y = faceThis.Y - position.Y;

                // we'll use the Atan2 function. Atan will calculates the arc tangent of 
                // y / x for us, and has the added benefit that it will use the signs of x
                // and y to determine what cartesian quadrant to put the result in.
                // http://msdn2.microsoft.com/en-us/library/system.math.atan2.aspx
                float desiredAngle = (float)Math.Atan2(y, x);

                // so now we know where we WANT to be facing, and where we ARE facing...
                // if we weren't constrained by turnSpeed, this would be easy: we'd just 
                // return desiredAngle.
                // instead, we have to calculate how much we WANT to turn, and then make
                // sure that's not more than turnSpeed.

                // first, figure out how much we want to turn, using WrapAngle to get our
                // result from -Pi to Pi ( -180 degrees to 180 degrees )
                float difference = WrapAngle(desiredAngle - currentAngle);

                // clamp that between -turnSpeed and turnSpeed.
                difference = MathHelper.Clamp(difference, -turnSpeed, turnSpeed);

                // so, the closest we can get to our target is currentAngle + difference.
                // return that, using WrapAngle again.
                return WrapAngle(currentAngle + difference);
            }

            /// <summary>
            /// Returns the angle expressed in radians between -Pi and Pi.
            /// <param name="radians">the angle to wrap, in radians.</param>
            /// <returns>the input value expressed in radians from -Pi to Pi.</returns>
            /// </summary>
            private static float WrapAngle(float radians)
            {
                while (radians < -MathHelper.Pi)
                {
                    radians += MathHelper.TwoPi;
                }
                while (radians > MathHelper.Pi)
                {
                    radians -= MathHelper.TwoPi;
                }
                return radians;
            }


        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // Look up inputs for the active player profile.
            int playerIndex = (int)ControllingPlayer.Value;

            KeyboardState keyboardState = input.CurrentKeyboardStates[playerIndex];
            GamePadState gamePadState = input.CurrentGamePadStates[playerIndex];

            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad. This requires us to keep track of
            // whether a gamepad was ever plugged in, because we don't want to pause
            // on PC if they are playing with a keyboard and have no gamepad at all!
            bool gamePadDisconnected = !gamePadState.IsConnected &&
                                       input.GamePadWasConnected[playerIndex];

            if (input.IsPauseGame(ControllingPlayer) || gamePadDisconnected)
            {
                ScreenManager.AddScreen(new PauseMenuScreen(), ControllingPlayer);
            }
           

        }
     
        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            // This game has a blue background. Why? Because!
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
                                               Color.Black, 0, 0);

            // Our player and enemy are both actually just text strings.
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

            spriteBatch.Begin();

            starField.Draw(spriteBatch);
            
            foreach (Shield s1 in player.shieldList)
                  s1.Draw(spriteBatch); 
            
            spriteBatch.Draw(ship, player.position, shipRect, Color.White, player.getRot(), new Vector2(9,12), 1, SpriteEffects.None, 0);
            spriteBatch.DrawString(gameFont, "Score: " + player.getScore(), scorePosition, Color.Green);
            spriteBatch.DrawString(gameFont, "Shields: " + player.getShields(), shieldPosition, Color.Blue);
            spriteBatch.DrawString(gameFont, "Boost: " + (int)player.getBoost(), boostPosition, Color.Red);
            spriteBatch.DrawString(gameFont, "Base Health: " + station.base_health, baseHealthPosition, Color.Blue);

            for (int i = 0; i < enemies.Count; i++)
            {
                enemies[i].Draw(spriteBatch);
            }

            for (int i = 0; i < projectiles.Count; i++)
            {
                projectiles[i].Draw(spriteBatch);
            }

            for (int i = 0; i < explosions.Count; i++)
            {
                explosions[i].Draw(spriteBatch);
            }

            station.Draw(spriteBatch);
            boltA.Draw(spriteBatch);
            turretA.Draw(spriteBatch);

            spriteBatch.End();

            if (TransitionPosition > 0 || pauseAlpha > 0)
            {
                float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);

                ScreenManager.FadeBackBufferToBlack(alpha);
            }
        }

        private void AddEnemy()
        {
            //Vector2 position = new Vector2(20, 40);
            Vector2 position = new Vector2(random.Next(200, ScreenManager.GraphicsDevice.Viewport.Width), 
                random.Next(200, ScreenManager.GraphicsDevice.Viewport.Height));
            
            Enemy enemy = new Enemy();

            enemy.Initialize(enemyTexture, position); //, screen);
            enemies.Add(enemy);
        }

        private void UpdateEnemies(GameTime gameTime)
        {
           if (enemies.Count == 0)
                for (int z = 0; z < maxEnemies; z++)
                    AddEnemy();

            for (int i = enemies.Count - 1; i >= 0; i--)
            {
                if (enemies[i].enemyState == Enemy.EnemyAiState.Attack && projectiles.Count < maxProjectiles)
                {
                    Vector2 left = new Vector2(1, 0);

                    Matrix rotMatrix = Matrix.CreateRotationZ(enemies[i].GetEnemyOrientation);
                    Vector2 velocity = Vector2.Transform(left, rotMatrix);

                    // pulled from turn2face function in enemy class.  Ensures enemy ship is pointed the right way before firing.
                    float x = station.main_basePosition.X - enemies[i].Position.X;
                    float y = station.main_basePosition.Y - enemies[i].Position.Y;

                    // we'll use the Atan2 function. Atan will calculates the arc tangent of 
                    // y / x for us, and has the added benefit that it will use the signs of x
                    // and y to determine what cartesian quadrant to put the result in.
                    // http://msdn2.microsoft.com/en-us/library/system.math.atan2.aspx
                    float desiredAngle = (float)Math.Atan2(y, x);

                    if (desiredAngle > enemies[i].GetEnemyOrientation - enemies[i].getOptimumFiringAngle && desiredAngle < enemies[i].GetEnemyOrientation + enemies[i].getOptimumFiringAngle)
                        AddProjectile(enemies[i].Position + new Vector2(30, 12), velocity, enemies[i].GetEnemyOrientation);

                    enemies[i].enemyState = Enemy.EnemyAiState.Escape;
                }
                else
                {
                    enemies[i].enemyState = Enemy.EnemyAiState.Wander;
                }


                if (projectiles.Count > 0)
                {
                    for (int j = projectiles.Count - 1; j >= 0; j--)
                    {
                        // enemy speed gets boosted for every projectile due to rerunning update again and again.  Bug or feature?
                        enemies[i].Update(gameTime, station.main_basePosition, projectiles[j].Position); //, ScreenManager);
                    }
                }
                else
                {
                    enemies[i].Update(gameTime, station.main_basePosition, Vector2.Zero); //, screen);
                }

                if (enemies[i].Active == false)
                {
                    AddExplosion(enemies[i].Position);
                    enemies.RemoveAt(i);
                }
                else
                {
                    enemies[i].Position = ClampToViewport(enemies[i].Position);
                }
            }
        }

        private Vector2 ClampToViewport(Vector2 vector)
        {
            Viewport vp = ScreenManager.GraphicsDevice.Viewport;
            vector.X = MathHelper.Clamp(vector.X, vp.X, vp.X + vp.Width);
            vector.Y = MathHelper.Clamp(vector.Y, vp.Y, vp.Y + vp.Height);
            return vector;
        }

        private void UpdateExplosions(GameTime gameTime)
        {
            for (int i = explosions.Count - 1; i >= 0; i--)
            {
                explosions[i].Update(gameTime);
                if (explosions[i].Active == false)
                {
                    explosions.RemoveAt(i);
                }
            }
        }

        private void AddProjectile(Vector2 position, Vector2 heading, float rotation)
        {
            Animation projectileAnimation = new Animation();
            //projectile.Initialize(ScreenManager.GraphicsDevice.Viewport, projectileTexture, position, heading, rotation);
            projectileAnimation.Initialize(projectileTexture, Vector2.Zero, 250, 224, 5, 30, Color.Aquamarine, .05f, true);
            projectile projectile = new projectile();

            projectile.Initialize(ScreenManager.GraphicsDevice.Viewport, projectileAnimation, position, heading);

            projectiles.Add(projectile);
        }

        private void UpdateProjectiles(GameTime gameTime)
        {
            for (int i = projectiles.Count - 1; i >= 0; i--)
            {
                projectiles[i].Update(gameTime);

                if (projectiles[i].Active == false)
                {
                    projectiles.RemoveAt(i);
                }
            }
        }

        private void UpdateCollision(GameTime gameTime)
        {
            //Use the Rectangle's built-in intersect function to
            //determine if two objects are overlapping
            Rectangle mainBaseRectangle;
            Rectangle projectileRectangle;
            Rectangle enemyRectangle;

            TimeSpan timer;

            //Only create the rectangle once for the player
            mainBaseRectangle = new Rectangle((int)station.main_basePosition.X,
            (int)station.main_basePosition.Y,
            station.Width,
            station.Height);

            //resctangle for bolt
            boltA.boltRect = new Rectangle((int)boltA.boltPosition.X, (int)boltA.boltPosition.Y,
            boltA.Width,
            boltA.Height);
                       
            //Do the collision between the projectile and the main base
            for (int i = 0; i < projectiles.Count; i++)
            {
                //projectileRectangle = new Rectangle((int)projectiles[i].Position.X,
                //    (int)projectiles[i].Position.Y, projectiles[i].Width / 2, projectiles[i].Height / 2);
                projectileRectangle = new Rectangle((int)projectiles[i].Position.X,
                    (int)projectiles[i].Position.Y, 5, 5); 
               
                //Determine if the two objects collided with each
                //other
                for (int k = 0; k < enemies.Count; k++)
                {
                    //enemyRectangle = new Rectangle((int)enemies[k].Position.X, (int)enemies[k].Position.Y,
                    //    enemies[k].Width / 2, enemies[k].Height / 2);
                    enemyRectangle = new Rectangle((int)enemies[k].Position.X, (int)enemies[k].Position.Y,
                        45, 40);

                    if (enemyRectangle.Intersects(projectileRectangle) && projectiles[i].Rebounded)
                    {
                        enemies[k].Active = false;
                        projectiles[i].Active = false;
                        lilexp.Play();
                        player.setScore(50); 
                        if (enemies.Count == 1) 
                            maxEnemies += 1;
                    }
                }
                
                if (mainBaseRectangle.Intersects(projectileRectangle))
                {
                    //station.Active = false;
                    AddLilExplosion(projectiles[i].Position);
                    lilexp.Play();
                    projectiles[i].Active = false;  
                    
                    //Subtract the health from the player based on
                    //the enemy damage
                    station.base_health -= projectiles[i].Damage;

                    //Since the enemy collided with the player
                    // destroy it
                    //projectiles[i].Health = 0;

                    //If the player health is less than zero we died
                    if (station.base_health <= 0)
                    {
                        station.Active = false;
                        turretA.Active = false;
                        AddExplosion(station.main_basePosition);
                        AddExplosion(station.main_basePosition + new Vector2(station.Width, station.Height));
                        AddExplosion(station.main_basePosition + new Vector2(station.Width, 0));
                        AddExplosion(station.main_basePosition + new Vector2(0, station.Height));

                        bigexp.Play();

                        end = true;
                        endtime = (int)gameTime.TotalGameTime.TotalMilliseconds;

                        //timer = TimeSpan.FromSeconds(3.0f);
                        //ScreenManager.AddScreen(new MainMenuScreen(), ControllingPlayer);

                    }
                }

                if (boltA.boltRect.Intersects(projectileRectangle))
                {
                    projectiles[i].Active = false;
                    bolthit.Play();
                    boltA.boltPosition.X = 394;
                    boltA.boltPosition.Y = 236;
                    boltA.Active = false;
                }

                for (int j = 0; j < player.shieldList.Count; j++)
                {

                    if (projectileRectangle.Intersects(player.shieldList[j].getRect()))
                    {
                       
                        projectiles[i].SetVelocity(Vector2.Reflect(projectiles[i].Velocity, player.shieldList[j].getNorm()));
                        AddLilExplosion(projectiles[i].Position);
                        shieldHit.Play();
                        projectiles[i].Rebounded = true;

                        if (j + 2 < player.shieldList.Count)
                            player.shieldList.RemoveAt(j + 2);
 
                        if (j+1 < player.shieldList.Count)
                                player.shieldList.RemoveAt(j + 1);                        
                        
                        if(player.shieldList.ElementAt(j) != null)
                                player.shieldList.RemoveAt(j);

                        if (j - 1 > 0)
                        {
                            if (player.shieldList.ElementAt(j - 1) != null)
                                player.shieldList.RemoveAt(j - 1);
                        }

                        if (j - 2 > 0)
                        {
                            if (player.shieldList.ElementAt(j - 2) != null)
                                player.shieldList.RemoveAt(j - 2);
                        }     
                    }
                }
            }
        }

        private void AddLilExplosion(Vector2 position)
        {
            Animation explosion = new Animation();
            explosion.Initialize(explosionTexture, position, 134, 134, 12, 45, Color.White, .5f, false);
            explosions.Add(explosion);
        }

        private void AddExplosion(Vector2 position)
        {
            Animation explosion = new Animation();
            explosion.Initialize(explosionTexture, position, 134, 134, 12, 45, Color.White, 1f, false);
            explosions.Add(explosion);
        }

        //private Vector2 TexturesCollide(Color[,] tex1, Matrix mat1, Color[,] tex2, Matrix mat2)
        //{
        //    Matrix mat1to2 = mat1 * Matrix.Invert(mat2);
        //    int width1 = tex1.GetLength(0);
        //    int height1 = tex1.GetLength(1);
        //    int width2 = tex2.GetLength(0);
        //    int height2 = tex2.GetLength(1);

        //    for (int x1 = 0; x1 < width1; x1++)
        //    {
        //        for (int y1 = 0; y1 < height1; y1++)
        //        {
        //            Vector2 pos1 = new Vector2(x1, y1);
        //            Vector2 pos2 = Vector2.Transform(pos1, mat1to2);

        //            int x2 = (int)pos2.X;
        //            int y2 = (int)pos2.Y;
        //            if ((x2 >= 0) && (x2 < width2))
        //            {
        //                if ((y2 >= 0) && (y2 < height2))
        //                {
        //                    if (tex1[x1, y1].A > 0)
        //                    {
        //                        if (tex2[x2, y2].A > 0)
        //                        {
        //                            Vector2 screenPos = Vector2.Transform(pos1, mat1);
        //                            return screenPos;
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //    }

        //    return new Vector2(-1, -1);
        //}


        #endregion
    }
}

